/**
 * Copyright 2008 Mathias Doenitz, http://lis.to/
 *
 * This file is part of the lis.to java desktop client. The lis.to java desktop client is free software: you can
 * redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * The lis.to java desktop client is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with the lis.to java desktop client.
 * If not, see http://www.gnu.org/licenses/
 */

package listo.client.taskfilters;

import com.google.inject.Inject;
import com.google.inject.Singleton;
import listo.client.model.ObjectId;
import listo.client.model.Tasks;
import listo.client.viewlists.*;
import listo.utils.types.KeyValuePair;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Singleton
public class SelectedViewFilter extends BaseFilter implements ViewListManager.ViewSelectionListener {

    private Tasks allTasks;
    private Map<ObjectId, Tasks> folderTasks;
    private Tasks trashTasks;

    private View[] selectedViews;

    @Inject
    public SelectedViewFilter(ViewListManager viewListManager, AllTasksFilter allTasksFilter,
                              FoldersFilter foldersFilter,
                              TrashFilter trashFilter) {
        viewListManager.addViewListSelectionListener(this);

        allTasksFilter.addFilterListener(new Listener() {
            public void tasksChanged(Object updateObject) {
                allTasks = (Tasks) updateObject;
                if (selectedViewsContains(AllTasksView.class)) update();
            }
        });

        foldersFilter.addFilterListener(new Listener() {
            @SuppressWarnings({"unchecked"})
            public void tasksChanged(Object updateObject) {
                folderTasks = (Map<ObjectId, Tasks>) updateObject;
                if (selectedViewsContains(FolderView.class)) update();
            }
        });

        trashFilter.addFilterListener(new Listener() {
            public void tasksChanged(Object updateObject) {
                trashTasks = (Tasks) updateObject;
                if (selectedViewsContains(TrashView.class)) update();
            }
        });
    }

    private boolean selectedViewsContains(Class clazz) {
        if (selectedViews != null) {
            for (View view : selectedViews) {
                if (view.getClass() == clazz) return true;
            }
        }
        return false;
    }

    public void viewSelectionChanged(View[] selectedViews) {
        this.selectedViews = selectedViews;
        update();
    }

    private void update() {
        Tasks tasks = new Tasks();

        // the selectedViews array might not be current anymore (maybe some tags have been deleted)
        // so remove all invalid entries in the selectedViews first
        List<View> views = new ArrayList<View>(selectedViews.length);
        for (View selectedView : selectedViews) {
            if (selectedView instanceof FolderView) {
                FolderView folderView = (FolderView) selectedView;
                if (!folderTasks.containsKey(folderView.getFolder().getId())) continue;
            }
            views.add(selectedView);
        }

        if (views.size() == 1) {
            // if we have only one selected view we dont need to filter
            Tasks selectedTasks = getViewTasks(views.get(0));
            if (selectedTasks != null) tasks.addAll(selectedTasks);
        }
        if (views.size() > 1) {
            // if we have several views selected we need to build the cross section of their tasks
            // first we sort the task lists of all selected views according to their task numbers
            List<KeyValuePair<Integer, Tasks>> viewTasks = new ArrayList<KeyValuePair<Integer, Tasks>>(views.size());
            for (View view : views) {
                Tasks t = getViewTasks(view);
                viewTasks.add(new KeyValuePair<Integer, Tasks>(t.size(), t));
            }
            Collections.sort(viewTasks);

            // perform a cross section across all other lists (based on the list with the fewest tasks for speed)
            tasks.addAll(viewTasks.get(0).getValue());
            for (int i = 1; i < viewTasks.size(); i++) {
                Tasks sectionTasks = viewTasks.get(i).getValue();
                for (int j = 0; j < tasks.size(); j++) {
                    if (!sectionTasks.contains(tasks.get(j))) tasks.remove(j--);
                }
            }
        }

        notifyListeners(tasks);
    }

    private Tasks getViewTasks(View view) {
        if (view instanceof AllTasksView) return allTasks;
        if (view instanceof TrashView) return trashTasks;

        FolderView folderView = (FolderView) view;
        return folderTasks.get(folderView.getFolder().getId());
    }

    public boolean isTrash(){
        return selectedViewsContains(TrashView.class);
    }

}